import multiprocessing
import re
import threading
import time

import psutil
from flask_socketio import emit
from werkzeug.security import generate_password_hash
from flask import render_template, current_app, request, jsonify, redirect, url_for, session
from . import main_bp
from create_app import redis_pool, socketio
from models import User
from opcua import ua, Server


@main_bp.route('/', methods=['GET', 'POST'])
def home():
    return render_template('home.html')


@main_bp.route('/dashboard', methods=['GET', 'POST'])
def dashboard():
    return render_template('dashboard.html')


@main_bp.route('/tables', methods=['GET', 'POST'])
def tables():
    return render_template('tables.html')


@main_bp.route('/3d', methods=['GET', 'POST'])
def view3d():
    return render_template('3d.html')


@main_bp.route('/opcua_server', methods=['GET', 'POST'])
def make_opcua_server():
    if request.method == 'POST':
        endpoint = request.form.get('endpoint')
        uri = request.form.get('uri')
        folder1_active = request.form.get('folder1_active')
        folder2_active = request.form.get('folder2_active')
        folder3_active = request.form.get('folder3_active')
        folder1 = request.form.get('folder1')
        folder2 = request.form.get('folder2')
        folder3 = request.form.get('folder3')
        folder1_tags = request.form.get('folder1_tags')
        folder2_tags = request.form.get('folder2_tags')
        folder3_tags = request.form.get('folder3_tags')
        # 整理数据
        address_space = {}
        if folder1_active == 'true':
            address_space[folder1] = folder1_tags.strip().split(' ')
        if folder2_active == 'true':
            address_space[folder2] = folder2_tags.strip().split(' ')
        if folder3_active == 'true':
            address_space[folder3] = folder3_tags.strip().split(' ')
        # 运行子进程
        try:
            opcua_server_backgroud_process(endpoint, uri, address_space)
        except ChildProcessError as e:
            current_app.logger.error(str(e))
            return jsonify({'status': False, 'message': str(e)})
        except Exception as e:
            current_app.logger.error(str(e))
            return jsonify({'status': False, 'message': str(e)})
        # # 从redis中获取到运行opcua server子进程的PID
        # opcua_server_pid = redis_pool.get('opcua_server_pid')
        # # 开启一个后端线程检测PID的状态
        # context = {}
        return jsonify({'status': True})
    return render_template('opcua_server.html')


def opcua_server_backgroud_process(endpoint, uri, address_space: dict):
    """
    启动运行OPCUA服务的后台进程
    :return:
    """
    # 创建一个子进程
    sub_process = multiprocessing.Process(target=opcua_server_backgroud_task, args=(endpoint, uri, address_space))
    # 设置子进程为守护进程。 web程序停止后，该进程自动销毁
    sub_process.daemon = True
    # 启动子进程
    sub_process.start()
    # 将子进程的pid写入到缓存中
    try:
        redis_pool.set('opcua_server_pid', sub_process.pid)
    except Exception as e:
        current_app.logger.error('cache opcua server pid failed!')
        raise ChildProcessError(e)


def opcua_server_backgroud_task(endpoint, uri, address_space: dict):
    """
    :param endpoint like opc.tcp://ip:port
    :param uri like https://www.baidu.com
    :param address_space lile {'folder_name': [tag_list in folder]}
    启动运行OPCUA服务的后台进程的任务
    :return:
    """
    # setup our server
    server = Server()
    server.set_endpoint(endpoint)

    # setup our own namespace, not really necessary but should as spec
    idx = server.register_namespace(uri)

    # get Objects node, this is where we should put our nodes
    objects = server.get_objects_node()

    # populating our address space
    for folder, tag_list in address_space.items():
        # 创建文件夹
        _folder = objects.add_folder(idx, folder)
        # 创建文件夹中的tag list
        for tag in tag_list:
            _tag = _folder.add_variable(idx, tag, 0.0)  # default 0.0
            # 将变量设置为可写
            _tag.set_writable()
    # starting!
    server.start()


@main_bp.route('/control_opcua_server', methods=['POST'])
def control_opcua_server():
    # 可以停止运行opcua server
    command = request.form.get('command')
    # 从redis中获取到opcua_server_pid
    try:
        opcua_server_pid = redis_pool.get('opcua_server_pid')
    except Exception as e:
        current_app.logger.error(str(e))
        return jsonify({'status': False, 'message': 'redis链接失败'})
    try:
        opcua_server_pid = opcua_server_pid.decode('utf-8')
        opcua_server_pid = int(opcua_server_pid)
    except UnicodeDecodeError:
        return jsonify({'status': False, 'message': 'decode error:' + opcua_server_pid})
    except ValueError:
        return jsonify({'status': False, 'message': f'PID must be int, not {type(opcua_server_pid)}'})

    if command == 'stop':
        p = psutil.Process(opcua_server_pid)
        p.kill()
        return jsonify({'status': True})
    return jsonify({'status': False, 'message': 'command is not allowed!'})
